package gogohome.ourhome;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class ByteUtils {
  private static final int DEFAULT_CHUNK_SIZE = 1024;
  private static final String[] hexStr = { "A", "B", "C", "D", "E", "F" };

  public static String toHex(byte[] bytes) {
		if (bytes == null || bytes.length == 0)
			return "";
		String hex = "";
		for (int i = 0; i < bytes.length; i++) {
			int num = 0xFF & bytes[i];
			int div = num / 16;
			int rem = num % 16;

			// 0xF9
			if (div > 9) {
				div -= 10;
				hex += " 0x" + hexStr[div];
			} else
				hex += " 0x" + div;

			if (rem > 9) {
				rem -= 10;
				hex += "" + hexStr[rem];
			} else
				hex += "" + rem;
		}
		return hex;
	}

	/**
	 * It will convert <= 4 bytes value to Int
	 * 
	 * @param bytes
	 * @param isBigEndian
	 *            - is it Big or Little Endian
	 * @return
	 */
	public static int toInt(byte[] bytes, boolean isBigEndian) {
		int x = 0;
		int numOfBytes = bytes.length;

		if (numOfBytes > 4)
			numOfBytes = 4; // Can't consider more than 4 bytes

		for (int i = 0; i < numOfBytes; i++) {
			if (i == 0) {
				if (isBigEndian)
					x = 0xFF & bytes[i]; // AND with 0xFF to ignore sign bit of
				// bytes to Int assignment
				else
					// Little Endian
					x = 0xFF & bytes[numOfBytes - 1];
			} else {
				if (isBigEndian)
					x = (x << 8) | (0xFF & bytes[i]);
				else
					x = (x << 8) | (0xFF & bytes[numOfBytes - 1 - i]);
			}
		}

		return x;
	}

	/**
	 * It will convert <= 8 bytes data into Long
	 * 
	 * @param bytes
	 * @param isBigEndian
	 *            is it Big or Little Endian
	 * @return
	 */
	public static long toLong(byte[] bytes, boolean isBigEndian) {
		long x = 0;
		int numOfBytes = bytes.length;

		if (numOfBytes > 8)
			numOfBytes = 8; // Can't consider more than 8 bytes

		for (int i = 0; i < numOfBytes; i++) {
			if (i == 0) {
				if (isBigEndian)
					x = 0xFF & bytes[i]; // AND with 0xFF to ignore sign bit of
				// bytes to Long assignment
				else
					// Little Endian
					x = 0xFF & bytes[numOfBytes - 1];
			} else {
				if (isBigEndian)
					x = (x << 8) | (0xFF & bytes[i]);
				else
					x = (x << 8) | (0xFF & bytes[numOfBytes - 1 - i]);
			}
		}

		return x;
	}
  

  /**
   * save bytes to file
   * @param fileName the file to write the supplied bytes
   * @param theBytes the bytes to write to file
   * @throws java.io.IOException reports problems saving bytes to file
   */
  public static void saveBytesToFile( String fileName, byte[] theBytes )
      throws java.io.IOException {
    saveBytesToStream( new java.io.FileOutputStream( fileName ), theBytes );
  }

  /**
   * save bytes to output stream and close the output stream on success and
   * on failure.
   * @param out the output stream to write the supplied bytes
   * @param theBytes the bytes to write out
   * @throws java.io.IOException reports problems saving bytes to output stream
   */
  public static void saveBytesToStream( java.io.OutputStream out, byte[] theBytes )
      throws java.io.IOException {
    try {
      out.write( theBytes );
    }
    finally {
      out.flush();
      out.close();
    }
  }

  /**
   * Loads bytes from the file
   *
   * @param fileName file to read the bytes from
   * @return bytes read from the file
   * @exception java.io.IOException reports IO failures
   */
  public static byte[] loadBytesFromFile( String fileName ) throws java.io.IOException {
    return loadBytesFromStream( new java.io.FileInputStream( fileName ), DEFAULT_CHUNK_SIZE );
  }

  /**
   * Loads bytes from the given input stream until the end of stream
   * is reached.  It reads in at kDEFAULT_CHUNK_SIZE chunks.
   *
   * @param stream to read the bytes from
   * @return bytes read from the stream
   * @exception java.io.IOException reports IO failures
   */
  public static byte[] loadBytesFromStream( java.io.InputStream in ) throws java.io.IOException {
    return loadBytesFromStream( in, DEFAULT_CHUNK_SIZE );
  }

  /**
   * Loads bytes from the given input stream until the end of stream
   * is reached.  Bytes are read in at the supplied <code>chunkSize</code>
   * rate.
   *
   * @param stream to read the bytes from
   * @return bytes read from the stream
   * @exception java.io.IOException reports IO failures
   */
  public static byte[] loadBytesFromStream( java.io.InputStream in, int chunkSize )
      throws java.io.IOException {
    if( chunkSize < 1 )
      chunkSize = DEFAULT_CHUNK_SIZE;

    int count;
    java.io.ByteArrayOutputStream bo = new java.io.ByteArrayOutputStream();
    byte[] b = new byte[chunkSize];
    try {
      while( ( count = in.read( b, 0, chunkSize ) ) > 0 ) {
        bo.write( b, 0, count );
      }
      byte[] thebytes = bo.toByteArray();
      return thebytes;
    }
    finally {
      bo.close();
    }
  }
}
